From the following blog post: http://katbailey.github.io/post/matrix-factorization-with-tensorflow/
In [ ]:
import tensorflow as tf
num_users = 10
rank = 10
num_items = 10
user_bias = 0
# Initialize the matrix factors from random normals with mean 0. W will
# represent users and H will represent items.
W = tf.Variable(tf.truncated_normal([num_users, rank], stddev=0.2, mean=0), name="users")
H = tf.Variable(tf.truncated_normal([rank, num_items], stddev=0.2, mean=0), name="items")
# To the user matrix we add a bias column holding the bias of each user,
# and another column of 1s to multiply the item bias by.
W_plus_bias = tf.concat(1, [W, tf.convert_to_tensor(user_bias, dtype=tf.float32, name="user_bias"), tf.ones((num_users,1), dtype=tf.float32, name="item_bias_ones")])
# To the item matrix we add a row of 1s to multiply the user bias by, and
# a bias row holding the bias of each item.
H_plus_bias = tf.concat(0, [H, tf.ones((1, num_items), name="user_bias_ones", dtype=float32), tf.convert_to_tensor(item_bias, dtype=tf.float32, name="item_bias")])
# Multiply the factors to get our result as a dense matrix
result = tf.matmul(W_plus_bias, H_plus_bias)
# Now we just want the values represented by the pairs of user and item
# indices for which we had known ratings. Unfortunately TensorFlow does not
# yet support numpy-like indexing of tensors. See the issue for this at
# https://github.com/tensorflow/tensorflow/issues/206 The workaround here
# came from https://github.com/tensorflow/tensorflow/issues/418 and is a
# little esoteric but in numpy this would just be done as follows:
# result_values = result[user_indices, item_indices]
result_values = tf.gather(tf.reshape(result, [-1]), user_indices * tf.shape(result)[1] + item_indices, name="extract_training_ratings")
# Same thing for the validation set ratings.
result_values_val = tf.gather(tf.reshape(result, [-1]), user_indices_val * tf.shape(result)[1] + item_indices_val, name="extract_validation_ratings")
# Calculate the difference between the predicted ratings and the actual
# ratings. The predicted ratings are the values obtained form the matrix
# multiplication with the mean rating added on.
diff_op = tf.sub(tf.add(result_values, mean_rating, name="add_mean"), rating_values, name="raw_training_error")
diff_op_val = tf.sub(tf.add(result_values_val, mean_rating, name="add_mean_val"), rating_values_val, name="raw_validation_error")
with tf.name_scope("training_cost") as scope:
base_cost = tf.reduce_sum(tf.square(diff_op, name="squared_difference"), name="sum_squared_error")
# Add regularization.
regularizer = tf.mul(tf.add(tf.reduce_sum(tf.square(W)), tf.reduce_sum(tf.square(H))), lda, name="regularize")
cost = tf.div(tf.add(base_cost, regularizer), num_ratings * 2, name="average_error")
with tf.name_scope("validation_cost") as scope:
cost_val = tf.div(tf.reduce_sum(tf.square(diff_op_val, name="squared_difference_val"), name="sum_squared_error_val"), num_ratings_val * 2, name="average_error")
# Use an exponentially decaying learning rate.
global_step = tf.Variable(0, trainable=False)
learning_rate = tf.train.exponential_decay(lr, global_step, 10000, 0.96, staircase=True)
with tf.name_scope("train") as scope:
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
# Passing global_step to minimize() will increment it at each step so
# that the learning rate will be decayed at the specified intervals.
train_step = optimizer.minimize(cost, global_step=global_step)
with tf.name_scope("training_accuracy") as scope:
# Just measure the absolute difference against the threshold
# TODO: support percentage-based thresholds
good = tf.less(tf.abs(diff_op), threshold)
accuracy_tr = tf.div(tf.reduce_sum(tf.cast(good, tf.float32)), num_ratings)
accuracy_tr_summary = tf.scalar_summary("accuracy_tr", accuracy_tr)
with tf.name_scope("validation_accuracy") as scope:
# Validation set accuracy:
good_val = tf.less(tf.abs(diff_op_val), threshold)
accuracy_val = tf.reduce_sum(tf.cast(good_val, tf.float32)) / num_ratings_val
accuracy_val_summary = tf.scalar_summary("accuracy_val", accuracy_val)
# Create a TensorFlow session and initialize variables.
sess = tf.Session()
sess.run(tf.initialize_all_variables())
# Make sure summaries get written to the logs.
summary_op = tf.merge_all_summaries()
writer = tf.train.SummaryWriter("/tmp/recommender_logs", sess.graph_def)
# Run the graph and see how we're doing on every 500th iteration.
for i in range(max_iter):
if i % 500 == 0:
res = sess.run([summary_op, accuracy_tr, accuracy_val, cost, cost_val])
summary_str = res[0]
acc_tr = res[1]
acc_val = res[2]
cost_ev = res[3]
cost_val_ev = res[4]
writer.add_summary(summary_str, i)
print("Training accuracy at step %s: %s" % (i, acc_tr))
print("Validation accuracy at step %s: %s" % (i, acc_val))
print("Training cost: %s" % (cost_ev))
print("Validation cost: %s" % (cost_val_ev))
else:
sess.run(train_step)
with tf.name_scope("final_model") as scope:
# At the end we want to get the final ratings matrix by adding the mean
# to the result matrix and doing any further processing required
add_mean_final = tf.add(result, mean_rating, name="add_mean_final")
if result_processor == None:
final_matrix = add_mean_final
else:
final_matrix = result_processor(add_mean_final)
final_res = sess.run([final_matrix])
In [ ]: